﻿
using ACMESharp.Crypto.JOSE;
using Microsoft.Extensions.Options;
using ProxyServer.Extensions;

namespace ProxyServer.Acme;

public class CheckForAcmeAccountState : AcmeState
{
    private readonly ILogger<CheckForAcmeAccountState> _logger;
    private readonly IAcmeStore _store;
    private readonly IOptions<AcmeOptions> _options;
    private readonly IHttpClientFactory _httpFactory;
    private readonly AcmeClientFactory _acmeFactory;

    public CheckForAcmeAccountState(
        AcmeStateMachineContext context,
        IAcmeStore store,
        IOptions<AcmeOptions> options,
        IHttpClientFactory httpFactory,
        AcmeClientFactory acmeFactory,
        ILogger<CheckForAcmeAccountState> logger) : base(context)
    {
        _store = store;
        _options = options;
        _httpFactory = httpFactory;
        _logger = logger;
        _acmeFactory = acmeFactory;
    }

    public override async Task<IAcmeState> MoveNextAsync(CancellationToken cancellationToken)
    {
        var exists = await _store.ExistsAsync(AcmeStoreConstants.ObjectKeys.AcmeAccountDetails, cancellationToken);

        if (exists)
        {
            _logger.LogInformation("Account already exists", _options.Value.Email);
            return MoveTo<TerminalState>();
        }

        if (_options.Value.Email.IsNullOrEmpty())
        {
            throw new InvalidOperationException("必须指定账户所属邮箱");
        }

        if (_options.Value.AcceptTermOfService == false)
        {
            throw new InvalidOperationException("必须同意服务协议才能创建账户");
        }

        var acme = await _acmeFactory.Create(cancellationToken: cancellationToken);

        _logger.LogInformation("Creating account for {@Emails}", _options.Value.Email);

        var account = await acme.CreateAccountAsync(_options.Value.Email.Select(x => "mailto:" + x), _options.Value.AcceptTermOfService, cancel: cancellationToken);
        var accountSigner = acme.Signer;
        var accountKey = new AccountKey(accountSigner.JwsAlg, accountSigner.Export());

        await _store.SaveAsync(account, AcmeStoreConstants.ObjectKeys.AcmeAccountDetails, cancellationToken);
        await _store.SaveAsync(accountKey, AcmeStoreConstants.ObjectKeys.AcmeAccountKey, cancellationToken);

        _logger.LogInformation("Created account for {@Eamil}", _options.Value.Email);

        return MoveTo<TerminalState>();
    }
}

public class AccountKey(string keyType, string keyExport)
{
    public string KeyType { get; set; } = keyType;
    public string KeyExport { get; set; } = keyExport;

    public IJwsTool GenerateTool()
    {
        if (KeyType.StartsWith("ES"))
        {
            var tool = new ACMESharp.Crypto.JOSE.Impl.ESJwsTool();
            tool.HashSize = int.Parse(KeyType.Substring(2));
            tool.Init();
            tool.Import(KeyExport);
            return tool;
        }

        if (KeyType.StartsWith("RS"))
        {
            var tool = new ACMESharp.Crypto.JOSE.Impl.RSJwsTool();
            tool.HashSize = int.Parse(KeyType.Substring(2));
            tool.Init();
            tool.Import(KeyExport);
            return tool;
        }

        throw new Exception($"Unknown or unsupported KeyType [{KeyType}]");
    }
}